Setup the Seurat
Object
This turorial is modified from Seurat.
For this tutorial, we will be analyzing a dataset of Peripheral Blood
Mononuclear Cells (PBMC) freely available from 10X Genomics. There are
2,700 single cells that were sequenced on the Illumina NextSeq 500. The
raw data can be found here.
We start by reading in the data. The Read10X() function
reads in the output of the cellranger
pipeline from 10X, returning a unique molecular identified (UMI) count
matrix. The values in this matrix represent the number of molecules for
each feature (i.e. gene; row) that are detected in each cell (column).
Note that more recent versions of cellranger now also output using the
h5
file format, which can be read in using the
Read10X_h5() function in Seurat.
We next use the count matrix to create a Seurat object.
The object serves as a container that contains both data (like the count
matrix) and analysis (like PCA, or clustering results) for a single-cell
dataset. For more information, check out our [Seurat object interaction
vignette], or our GitHub Wiki. For
example, in Seurat v5, the count matrix is stored in
pbmc[["RNA"]]$counts.
library(dplyr)
library(Seurat)
library(patchwork)
# Load the PBMC dataset
pbmc.data <- Read10X(data.dir = "data/hg19/")
# Initialize the Seurat object with the raw (non-normalized data).
pbmc <- CreateSeuratObject(counts = pbmc.data, project = "pbmc3k", min.cells = 3, min.features = 200)
pbmc
#> An object of class Seurat
#> 13714 features across 2700 samples within 1 assay
#> Active assay: RNA (13714 features, 0 variable features)
#> 1 layer present: counts
What does data in a count matrix look like?
# Lets examine a few genes in the first thirty cells
pbmc.data[c("CD3D","TCL1A","MS4A1"), 1:30]
#> 3 x 30 sparse Matrix of class "dgCMatrix"
#>
#> CD3D 4 . 10 . . 1 2 3 1 . . 2 7 1 . . 1 3 . 2 3 . . . . . 3 4 1 5
#> TCL1A . . . . . . . . 1 . . . . . . . . . . . . 1 . . . . . . . .
#> MS4A1 . 6 . . . . . . 1 1 1 . . . . . . . . . 36 1 2 . . 2 . . . .
The . values in the matrix represent 0s (no molecules
detected). Since most values in an scRNA-seq matrix are 0, Seurat uses a
sparse-matrix representation whenever possible. This results in
significant memory and speed savings for Drop-seq/inDrop/10x data.
dense.size <- object.size(as.matrix(pbmc.data))
dense.size
#> 709591472 bytes
sparse.size <- object.size(pbmc.data)
sparse.size
#> 29905192 bytes
#> 23.7 bytes
Standard pre-processing
workflow
The steps below encompass the standard pre-processing workflow for
scRNA-seq data in Seurat. These represent the selection and filtration
of cells based on QC metrics, data normalization and scaling, and the
detection of highly variable features.
QC and selecting
cells for further analysis
Seurat allows you to easily explore QC metrics and filter cells based
on any user-defined criteria. A few QC metrics commonly
used by the community include
- The number of unique genes detected in each cell.
- Low-quality cells or empty droplets will often have very few
genes
- Cell doublets or multiplets may exhibit an aberrantly high gene
count
- Similarly, the total number of molecules detected within a cell
(correlates strongly with unique genes)
- The percentage of reads that map to the mitochondrial genome
- Low-quality / dying cells often exhibit extensive mitochondrial
contamination
- We calculate mitochondrial QC metrics with the
PercentageFeatureSet() function, which calculates the
percentage of counts originating from a set of features
- We use the set of all genes starting with
MT- as a set
of mitochondrial genes
# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
pbmc[["percent.mt"]] <- PercentageFeatureSet(pbmc, pattern = "^MT-")
Where are QC metrics stored in Seurat?
- The number of unique genes and total molecules are automatically
calculated during
CreateSeuratObject()
- You can find them stored in the object meta data
# Show QC metrics for the first 5 cells
head(pbmc@meta.data, 5)
In the example below, we visualize QC metrics, and use these to
filter cells.
- We filter cells that have unique feature counts over 2,500 or less
than 200
- We filter cells that have >5% mitochondrial counts
#Visualize QC metrics as a violin plot
VlnPlot(pbmc, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)

# FeatureScatter is typically used to visualize feature-feature relationships, but can be used for anything calculated by the object, i.e. columns in object metadata, PC scores etc.
plot1 <- FeatureScatter(pbmc, feature1 = "nCount_RNA", feature2 = "percent.mt")
plot2 <- FeatureScatter(pbmc, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")
plot1 + plot2

pbmc <- subset(pbmc, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5)
Normalizing the
data
After removing unwanted cells from the dataset, the next step is to
normalize the data. By default, we employ a global-scaling normalization
method “LogNormalize” that normalizes the feature expression
measurements for each cell by the total expression, multiplies this by a
scale factor (10,000 by default), and log-transforms the result. In
Seurat v5, Normalized values are stored in
pbmc[["RNA"]]$data.
pbmc <- NormalizeData(pbmc, normalization.method = "LogNormalize", scale.factor = 1e4)
For clarity, in this previous line of code (and in future commands),
we provide the default values for certain parameters in the function
call. However, this isn’t required and the same behavior can be achieved
with:
pbmc <- NormalizeData(pbmc)
While this method of normalization is standard and widely used in
scRNA-seq analysis, global-scaling relies on an assumption that each
cell originally contains the same number of RNA molecules. We and others
have developed alternative workflows for the single cell preprocessing
that do not make these assumptions. For users who are interested, please
check out our SCTransform() normalization workflow. The
method is described in ourpaper,
with a separate vignette using Seurat here.
The use of SCTransform replaces the need to run
NormalizeData, FindVariableFeatures, or
ScaleData (described below.)
Identification of
highly variable features (feature selection)
We next calculate a subset of features that exhibit high cell-to-cell
variation in the dataset (i.e, they are highly expressed in some cells,
and lowly expressed in others). We and others have found
that focusing on these genes in downstream analysis helps to highlight
biological signal in single-cell datasets.
Our procedure in Seurat is described in detail here, and improves
on previous versions by directly modeling the mean-variance relationship
inherent in single-cell data, and is implemented in the
FindVariableFeatures() function. By default, we return
2,000 features per dataset. These will be used in downstream analysis,
like PCA.
pbmc <- FindVariableFeatures(pbmc, selection.method = 'vst', nfeatures = 2000)
# Identify the 10 most highly variable genes
top10 <- head(VariableFeatures(pbmc), 10)
# plot variable features with and without labels
plot1 <- VariableFeaturePlot(pbmc)
plot2 <- LabelPoints(plot = plot1, points = top10, repel = TRUE)
plot1 + plot2

Scaling the data
Next, we apply a linear transformation (‘scaling’) that is a standard
pre-processing step prior to dimensional reduction techniques like PCA.
The ScaleData() function:
- Shifts the expression of each gene, so that the mean expression
across cells is 0
- Scales the expression of each gene, so that the variance across
cells is 1
- This step gives equal weight in downstream analyses, so that
highly-expressed genes do not dominate
- The results of this are stored in
pbmc[["RNA"]]$scale.data
- By default, only variable features are scaled.
- You can specify the
features argument to scale
additional features
all.genes <- rownames(pbmc)
pbmc <- ScaleData(pbmc, features = all.genes)
How can I remove unwanted sources of variation
In Seurat, we also use the ScaleData() function to
remove unwanted sources of variation from a single-cell dataset. For
example, we could ‘regress out’ heterogeneity associated with (for
example) cell
cycle stage, or mitochondrial contamination i.e.:
pbmc <- ScaleData(pbmc, vars.to.regress = 'percent.mt')
However, particularly for advanced users who would like to use this
functionality, we strongly recommend the use of our new normalization
workflow, SCTransform(). The method is described in our paper,
with a separate vignette using Seurat here.
As with ScaleData(), the function
SCTransform() also includes a vars.to.regress
parameter.
Perform linear
dimensional reduction
Next we perform PCA on the scaled data. By default, only the
previously determined variable features are used as input, but can be
defined using features argument if you wish to choose a
different subset (if you do want to use a custom subset of features,
make sure you pass these to ScaleData first).
For the first principal components, Seurat outputs a list of genes
with the most positive and negative loadings, representing modules of
genes that exhibit either correlation (or anti-correlation) across
single-cells in the dataset.
pbmc <- RunPCA(pbmc, features = VariableFeatures(object = pbmc))
Seurat provides several useful ways of visualizing both cells and
features that define the PCA, including VizDimReduction(),
DimPlot(), and DimHeatmap()
# Examine and visualize PCA results a few different ways
print(pbmc[['pca']], dims = 1:5, nfeatures = 5)
#> PC_ 1
#> Positive: CST3, TYROBP, LST1, AIF1, FTL
#> Negative: MALAT1, LTB, IL32, IL7R, CD2
#> PC_ 2
#> Positive: CD79A, MS4A1, TCL1A, HLA-DQA1, HLA-DQB1
#> Negative: NKG7, PRF1, CST7, GZMB, GZMA
#> PC_ 3
#> Positive: HLA-DQA1, CD79A, CD79B, HLA-DQB1, HLA-DPB1
#> Negative: PPBP, PF4, SDPR, SPARC, GNG11
#> PC_ 4
#> Positive: HLA-DQA1, CD79B, CD79A, MS4A1, HLA-DQB1
#> Negative: VIM, IL7R, S100A6, IL32, S100A8
#> PC_ 5
#> Positive: GZMB, NKG7, S100A8, FGFBP2, GNLY
#> Negative: LTB, IL7R, CKB, VIM, MS4A7
VizDimLoadings(pbmc, dims = 1:2, reduction = 'pca')

DimPlot(pbmc, reduction = 'pca') + NoLegend()

In particular DimHeatmap() allows for easy exploration
of the primary sources of heterogeneity in a dataset, and can be useful
when trying to decide which PCs to include for further downstream
analyses. Both cells and features are ordered according to their PCA
scores. Setting cells to a number plots the ‘extreme’ cells
on both ends of the spectrum, which dramatically speeds plotting for
large datasets. Though clearly a supervised analysis, we find this to be
a valuable tool for exploring correlated feature sets.
DimHeatmap(pbmc, dims = 1, cells = 500, balanced = TRUE)

DimHeatmap(pbmc, dims = 1:15, cells = 500, balanced = TRUE)

Determine the
‘dimensionality’ of the dataset
To overcome the extensive technical noise in any single feature for
scRNA-seq data, Seurat clusters cells based on their PCA scores, with
each PC essentially representing a ‘metafeature’ that combines
information across a correlated feature set. The top principal
components therefore represent a robust compression of the dataset.
However, how many components should we choose to include? 10? 20?
100?
In Macosko et
al, we implemented a resampling test inspired by the JackStraw
procedure. While still available in Seurat (see
previous vignette), this is a slow and computationally expensive
procedure, and we is no longer routinely used in single cell
analysis.
An alternative heuristic method generates an ‘Elbow plot’: a ranking
of principle components based on the percentage of variance explained by
each one (ElbowPlot() function). In this example, we can
observe an ‘elbow’ around PC9-10, suggesting that the majority of true
signal is captured in the first 10 PCs.

Identifying the true dimensionality of a dataset – can be
challenging/uncertain for the user. We therefore suggest these multiple
approaches for users. The first is more supervised, exploring PCs to
determine relevant sources of heterogeneity, and could be used in
conjunction with GSEA for example. The second (ElbowPlot)
The third is a heuristic that is commonly used, and can be calculated
instantly. In this example, we might have been justified in choosing
anything between PC 7-12 as a cutoff.
We chose 10 here, but encourage users to consider the following:
- Dendritic cell and NK aficionados may recognize that genes strongly
associated with PCs 12 and 13 define rare immune subsets (i.e. MZB1 is a
marker for plasmacytoid DCs). However, these groups are so rare, they
are difficult to distinguish from background noise for a dataset of this
size without prior knowledge.
- We encourage users to repeat downstream analyses with a different
number of PCs (10, 15, or even 50!). As you will observe, the results
often do not differ dramatically.
- We advise users to err on the higher side when choosing this
parameter. For example, performing downstream analyses with only 5 PCs
does significantly and adversely affect results.
Cluster the cells
Seurat applies a graph-based clustering approach, building upon
initial strategies in (Macosko et
al). Importantly, the distance metric which drives the
clustering analysis (based on previously identified PCs) remains the
same. However, our approach to partitioning the cellular distance matrix
into clusters has dramatically improved. Our approach was heavily
inspired by recent manuscripts which applied graph-based clustering
approaches to scRNA-seq data [SNN-Cliq,
Xu and Su, Bioinformatics, 2015] and CyTOF data [PhenoGraph, Levine
et al., Cell, 2015]. Briefly, these methods embed cells in
a graph structure - for example a K-nearest neighbor (KNN) graph, with
edges drawn between cells with similar feature expression patterns, and
then attempt to partition this graph into highly interconnected
‘quasi-cliques’ or ‘communities’.
As in PhenoGraph, we first construct a KNN graph based on the
euclidean distance in PCA space, and refine the edge weights between any
two cells based on the shared overlap in their local neighborhoods
(Jaccard similarity). This step is performed using the
FindNeighbors() function, and takes as input the previously
defined dimensionality of the dataset (first 10 PCs).
To cluster the cells, we next apply modularity optimization
techniques such as the Louvain algorithm (default) or SLM [SLM, Blondel
et al., Journal of Statistical Mechanics], to iteratively
group cells together, with the goal of optimizing the standard
modularity function. The FindClusters() function implements
this procedure, and contains a resolution parameter that sets the
‘granularity’ of the downstream clustering, with increased values
leading to a greater number of clusters. We find that setting this
parameter between 0.4-1.2 typically returns good results for single-cell
datasets of around 3K cells. Optimal resolution often increases for
larger datasets. The clusters can be found using the
Idents() function.
pbmc <- FindNeighbors(pbmc, dims = 1:10)
pbmc <- FindClusters(pbmc, resolution = 0.5)
#> Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
#>
#> Number of nodes: 2638
#> Number of edges: 95927
#>
#> Running Louvain algorithm...
#> Maximum modularity in 10 random starts: 0.8728
#> Number of communities: 9
#> Elapsed time: 0 seconds
# Look at cluster IDs of the first 5 cells
head(Idents(pbmc), 5)
#> AAACATACAACCAC-1 AAACATTGAGCTAC-1 AAACATTGATCAGC-1 AAACCGTGCTTCCG-1
#> 2 3 2 1
#> AAACCGTGTATGCG-1
#> 6
#> Levels: 0 1 2 3 4 5 6 7 8
Run non-linear
dimensional reduction (UMAP/tSNE)
Seurat offers several non-linear dimensional reduction techniques,
such as tSNE and UMAP, to visualize and explore these datasets. The goal
of these algorithms is to learn underlying structure in the dataset, in
order to place similar cells together in low-dimensional space.
Therefore, cells that are grouped together within graph-based clusters
determined above should co-localize on these dimension reduction
plots.
While we and others have routinely found 2D visualization techniques
like tSNE and UMAP to be valuable tools for exploring datasets, all
visualization techniques have limitations, and cannot fully represent
the complexity of the underlying data. In particular, these methods aim
to preserve local distances in the dataset (i.e. ensuring that cells
with very similar gene expression profiles co-localize), but often do
not preserve more global relationships. We encourage users to leverage
techniques like UMAP for visualization, but to avoid drawing biological
conclusions solely on the basis of visualization techniques.
pbmc <- RunUMAP(pbmc, dims = 1:10)
# note that you can set `label = TRUE` or use the LabelClusters function to help label individual clusters
DimPlot(pbmc, reduction = 'umap')

You can save the object at this point so that it can easily be loaded
back in without having to rerun the computationally intensive steps
performed above, or easily shared with collaborators.
saveRDS(pbmc, file = "data/pbmc_tutorial.rds")
Finding differentially
expressed features (cluster biomarkers)
Seurat can help you find markers that define clusters via
differential expression (DE). By default, it identifies positive and
negative markers of a single cluster (specified in
ident.1), compared to all other cells.
FindAllMarkers() automates this process for all clusters,
but you can also test groups of clusters vs. each other, or against all
cells.
In Seurat v5, we use the presto package (as described here and
available for installation here), to
dramatically improve the speed of DE analysis, particularly for large
datasets. For users who are not using presto, you can examine the
documentation for this function (?FindMarkers) to explore
the min.pct and logfc.threshold parameters,
which can be increased in order to increase the speed of DE testing.
# find all markers of cluster 2
cluster2.markers <- FindMarkers(pbmc, ident.1 = 2)
head(cluster2.markers, n = 5)
# find all markers distinguishing cluster 5 from clusters 0 and 3
cluster5.markers <- FindMarkers(pbmc, ident.1 = 5, ident.2 = c(0, 3))
head(cluster5.markers, n = 5)
# find markers for every cluster compared to all remaining cells, report only the positive ones
pbmc.markers <- FindAllMarkers(pbmc, only.pos = TRUE)
pbmc.markers %>%
group_by(cluster) %>%
dplyr::filter(avg_log2FC > 1)
Seurat has several tests for differential expression which can be set
with the test.use parameter (see our DE
vignette for details). For example, the ROC test returns the
‘classification power’ for any individual marker (ranging from 0 -
random, to 1 - perfect).
cluster0.markers <- FindMarkers(pbmc, ident.1 = 0, logfc.threshold = 0.25, test.use = "roc", only.pos = TRUE)
We include several tools for visualizing marker expression.
VlnPlot() (shows expression probability distributions
across clusters), and FeaturePlot() (visualizes feature
expression on a tSNE or PCA plot) are our most commonly used
visualizations. We also suggest exploring RidgePlot(),
CellScatter(), and DotPlot() as additional
methods to view your dataset.
VlnPlot(pbmc, features = c("MS4A1", "CD79A"))

# you can plot raw counts as well
VlnPlot(pbmc, features = c("NKG7", "PF4"), slot = 'counts', log = TRUE)

FeaturePlot(pbmc, features = c("MS4A1", "GNLY", "CD3E", "CD14", "FCER1A", "FCGR3A", "LYZ", "PPBP", "CD8A"))
DoHeatmap() generates an expression heatmap for given cells
and features. In this case, we are plotting the top 10 markers (or all
markers if less than 10) for each cluster.
pbmc.markers %>%
group_by(cluster) %>%
dplyr::filter(avg_log2FC > 1) %>%
slice_head(n = 10) %>%
ungroup() -> top10
DoHeatmap(pbmc, features = top10$gene) + NoLegend()

# Define the features (genes) you want to plot
# Note: CD3D is a T-cell marker, FCGR3A is a marker for FCGR3A+ Monocytes,
# and GNLY is a marker for Natural Killer cells
features_to_plot <- c("CD3D", "FCGR3A", "GNLY")
# Create a standard ridge plot
RidgePlot(object = pbmc, features = features_to_plot)

# Create a stacked ridge plot for comparison
RidgePlot(
object = pbmc_small,
features = features_to_plot,
stack = TRUE,
cols = c("red", "blue", "green")
)

# Compare two individual cells from the dataset
CellScatter(
object = pbmc,
cell1 = "AAACATACAACCAC-1",
cell2 = "AAACATTGAGCTAC-1"
)

# Create a DotPlot for a set of marker genes across the default identity classes
DotPlot(
object = pbmc,
features = c("MS4A1", "CD79A", "CD3D", "CCR7", "CD14", "FCER1A")
)

Assigning cell type
identity to clusters
Fortunately in the case of this dataset, we can use canonical markers
to easily match the unbiased clustering to known cell types:
| 0 |
IL7R, CCR7 |
Naive CD4+ T |
| 1 |
CD14, LYZ |
CD14+ Mono |
| 2 |
IL7R, S100A4 |
Memory CD4+ |
| 3 |
MS4A1 |
B |
| 4 |
CD8A |
CD8+ T |
| 5 |
FCGR3A, MS4A7 |
FCGR3A+ Mono |
| 6 |
GNLY, NKG7 |
NK |
| 7 |
FCER1A, CST3 |
DC |
| 8 |
PPBP |
Platelet |
new.cluster.ids <- c("Naive CD4 T", "CD14+ Mono", "Memory CD4 T", "B", "CD8 T", "FCGR3A+ Mono", "NK", "DC", "Platelet")
names(new.cluster.ids) <- levels(pbmc)
pbmc <- RenameIdents(pbmc, new.cluster.ids)
DimPlot(pbmc, reduction = 'umap', label = TRUE, pt.size = 0.5) + NoLegend()

library(ggplot2)
plot <- DimPlot(pbmc, reduction = "umap", label = TRUE, label.size = 4.5) + xlab("UMAP 1") + ylab("UMAP 2") +
theme(axis.title = element_text(size = 18), legend.text = element_text(size = 18)) +
guides(colour = guide_legend(override.aes = list(size = 10)))
ggsave(filename = "../output/images/pbmc3k_umap.png", height = 7, width = 12, plot = plot, dpi = 300)
saveRDS(pbmc, file = "../output/pbmc3k_final.rds")
Session Info
#> R version 4.5.0 (2025-04-11 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 22000)
#>
#> Matrix products: default
#> LAPACK version 3.12.1
#>
#> locale:
#> [1] LC_COLLATE=English_United States.utf8
#> [2] LC_CTYPE=English_United States.utf8
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C
#> [5] LC_TIME=English_United States.utf8
#>
#> time zone: America/New_York
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] ggplot2_4.0.0 future_1.67.0 patchwork_1.3.2 Seurat_5.3.0
#> [5] SeuratObject_5.2.0 sp_2.2-0 dplyr_1.1.4 klippy_0.0.0.9500
#>
#> loaded via a namespace (and not attached):
#> [1] RColorBrewer_1.1-3 rstudioapi_0.17.1 jsonlite_2.0.0
#> [4] magrittr_2.0.4 spatstat.utils_3.2-0 ggbeeswarm_0.7.2
#> [7] farver_2.1.2 rmarkdown_2.29 ragg_1.5.0
#> [10] vctrs_0.6.5 ROCR_1.0-11 spatstat.explore_3.5-3
#> [13] htmltools_0.5.8.1 sass_0.4.10 sctransform_0.4.2
#> [16] parallelly_1.45.1 KernSmooth_2.23-26 bslib_0.9.0
#> [19] htmlwidgets_1.6.4 ica_1.0-3 plyr_1.8.9
#> [22] plotly_4.11.0 zoo_1.8-14 cachem_1.1.0
#> [25] igraph_2.1.4 mime_0.13 lifecycle_1.0.4
#> [28] pkgconfig_2.0.3 Matrix_1.7-4 R6_2.6.1
#> [31] fastmap_1.2.0 fitdistrplus_1.2-4 shiny_1.11.1
#> [34] digest_0.6.37 tensor_1.5.1 RSpectra_0.16-2
#> [37] irlba_2.3.5.1 textshaping_1.0.3 labeling_0.4.3
#> [40] progressr_0.16.0 spatstat.sparse_3.1-0 httr_1.4.7
#> [43] polyclip_1.10-7 abind_1.4-8 compiler_4.5.0
#> [46] withr_3.0.2 S7_0.2.0 fastDummies_1.7.5
#> [49] R.utils_2.13.0 MASS_7.3-65 tools_4.5.0
#> [52] vipor_0.4.7 lmtest_0.9-40 beeswarm_0.4.0
#> [55] httpuv_1.6.16 future.apply_1.20.0 goftest_1.2-3
#> [58] R.oo_1.27.1 glue_1.8.0 nlme_3.1-168
#> [61] promises_1.3.3 grid_4.5.0 Rtsne_0.17
#> [64] cluster_2.1.8.1 reshape2_1.4.4 generics_0.1.4
#> [67] gtable_0.3.6 spatstat.data_3.1-8 R.methodsS3_1.8.2
#> [70] tidyr_1.3.1 data.table_1.17.8 spatstat.geom_3.6-0
#> [73] RcppAnnoy_0.0.22 ggrepel_0.9.6 RANN_2.6.2
#> [76] pillar_1.11.1 stringr_1.5.2 spam_2.11-1
#> [79] RcppHNSW_0.6.0 limma_3.64.3 later_1.4.4
#> [82] splines_4.5.0 lattice_0.22-7 survival_3.8-3
#> [85] deldir_2.0-4 tidyselect_1.2.1 miniUI_0.1.2
#> [88] pbapply_1.7-4 knitr_1.50 gridExtra_2.3
#> [91] scattermore_1.2 xfun_0.52 statmod_1.5.0
#> [94] matrixStats_1.5.0 stringi_1.8.7 lazyeval_0.2.2
#> [97] yaml_2.3.10 evaluate_1.0.5 codetools_0.2-20
#> [100] tibble_3.3.0 cli_3.6.5 uwot_0.2.3
#> [103] xtable_1.8-4 reticulate_1.43.0 systemfonts_1.2.3
#> [106] jquerylib_0.1.4 dichromat_2.0-0.1 Rcpp_1.1.0
#> [109] globals_0.18.0 spatstat.random_3.4-2 png_0.1-8
#> [112] ggrastr_1.0.2 spatstat.univar_3.1-4 parallel_4.5.0
#> [115] assertthat_0.2.1 presto_1.0.0 dotCall64_1.2
#> [118] listenv_0.9.1 viridisLite_0.4.2 scales_1.4.0
#> [121] ggridges_0.5.7 purrr_1.1.0 rlang_1.1.6
#> [124] cowplot_1.2.0
LS0tDQp0aXRsZTogIlNldXJhdCAtIEd1aWRlZCBDbHVzdGVyaW5nIFR1dG9yaWFsIg0KYXV0aG9yOiAiSG9uZ2NoZW4gWGlhbyINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmJpYmxpb2dyYXBoeTogWyJCaW9pbmZvcm1hdGljcy5iaWIiXQ0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBlY2hvID0gVFJVRSwNCiAgd2FybmluZyA9IEZBTFNFLA0KICBtZXNzYWdlID0gRkFMU0UsDQogIGVycm9yID0gRkFMU0UsDQogIGNvbW1lbnQgPSAiIz4iLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIiwNCiAgb3V0LndpZHRoID0gIjgwJSIsDQogIGRwaSA9IDMwMCwNCiAgY2FjaGUgPSBUUlVFDQopDQoNCmBgYA0KDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KbGlicmFyeShrbGlwcHkpDQogICAga2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCdib3R0b20nLCAncmlnaHQnKSwNCiAgICAgICAgICAgICAgICAgICBsYW5nID0gYygncicsICdweXRob24nLCAnYmFzaCcpLA0KICAgICAgICAgICAgICAgICAgIHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgDQogICAgICAgICAgICAgICAgICAgdG9vbHRpcF9zdWNjZXNzID0gJ0NvcGllZCEnLA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ2RhcmtSZWQnKQ0KYGBgDQoNCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCi8qIEN1c3RvbSBDU1Mgc3R5bGluZyAqLw0KYm9keSB7DQogIGZvbnQtZmFtaWx5OiAnSGVsdmV0aWNhIE5ldWUnLCBBcmlhbCwgc2Fucy1zZXJpZjsNCiAgbGluZS1oZWlnaHQ6IDEuODsNCiAgbWF4LXdpZHRoOiAxMjAwcHg7DQogIG1hcmdpbjogYXV0bzsNCiAgcGFkZGluZzogMmVtOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmOw0KICBjb2xvcjogIzJjM2U1MDsNCn0NCg0KaDEsIGgyLCBoMywgaDQgew0KICBjb2xvcjogIzJjM2U1MDsNCiAgZm9udC13ZWlnaHQ6IDYwMDsNCiAgbWFyZ2luLXRvcDogMmVtOw0KICBib3JkZXItYm90dG9tOiAycHggc29saWQgI2VhZWNlZjsNCiAgcGFkZGluZy1ib3R0b206IDAuM2VtOw0KfQ0KDQoudGl0bGUgew0KICBjb2xvcjogIzJjM2U1MDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXNpemU6IDIuOGVtOw0KICBtYXJnaW4tYm90dG9tOiAxZW07DQogIGJvcmRlci1ib3R0b206IG5vbmU7DQogIGZvbnQtd2VpZ2h0OiA3MDA7DQp9DQoNCi5hdXRob3IsIC5kYXRlIHsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBjb2xvcjogIzdmOGM4ZDsNCiAgbWFyZ2luLWJvdHRvbTogMmVtOw0KfQ0KDQovKiBDb2RlIGJsb2NrcyAqLw0KcHJlIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYSAhaW1wb3J0YW50Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZTllY2VmOw0KICBib3JkZXItcmFkaXVzOiA0cHg7DQogIHBhZGRpbmc6IDFlbTsNCiAgbWFyZ2luOiAxZW0gMDsNCiAgb3ZlcmZsb3cteDogYXV0bzsNCn0NCg0KY29kZSB7DQogIGNvbG9yOiAjZTgzZThjOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmOWZhOw0KICBwYWRkaW5nOiAycHggNHB4Ow0KICBib3JkZXItcmFkaXVzOiAzcHg7DQp9DQoNCnByZS5yOmJlZm9yZSB7DQogIGNvbnRlbnQ6ICJSIFNjcmlwdCI7DQogIGRpc3BsYXk6IGJsb2NrOyAvKiBNYWtlIHRoZSBsYWJlbCBhcHBlYXIgb24gaXRzIG93biBsaW5lICovDQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogIzU1NTsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTsNCiAgcGFkZGluZzogNXB4DQp9DQoNCnByZS5iYXNoOmJlZm9yZSB7DQogIGNvbnRlbnQ6ICJCYXNoIFNjcmlwdCI7DQogIGRpc3BsYXk6IGJsb2NrOyAvKiBNYWtlIHRoZSBsYWJlbCBhcHBlYXIgb24gaXRzIG93biBsaW5lICovDQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogIzU1NTsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTsNCiAgcGFkZGluZzogNXB4Ow0KfQ0KDQovKiBUYWJsZSBvZiBDb250ZW50cyBzdHlsaW5nICovDQojVE9DIHsNCiAgcGFkZGluZzogMjBweDsNCiAgYmFja2dyb3VuZDogI2Y4ZjlmYTsNCiAgYm9yZGVyLXJhZGl1czogNXB4Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZTllY2VmOw0KfQ0KDQoudG9jaWZ5IHsNCiAgYm9yZGVyOiBub25lOw0KfQ0KDQoudG9jaWZ5IC50b2NpZnktaXRlbSBhIHsNCiAgY29sb3I6ICMyYzNlNTA7DQp9DQoNCi50b2NpZnkgLnRvY2lmeS1pdGVtLmFjdGl2ZSBhIHsNCiAgY29sb3I6ICMwMDdiZmY7DQogIGJvcmRlci1sZWZ0OiAzcHggc29saWQgIzAwN2JmZjsNCn0NCg0KLyogSW1hZ2VzICovDQppbWcgew0KICBtYXgtd2lkdGg6IDEwMCU7DQogIGhlaWdodDogYXV0bzsNCiAgbWFyZ2luOiAyZW0gYXV0bzsNCiAgZGlzcGxheTogYmxvY2s7DQogIGJveC1zaGFkb3c6IDAgNHB4IDZweCByZ2JhKDAsIDAsIDAsIDAuMSk7DQogIGJvcmRlci1yYWRpdXM6IDRweDsNCn0NCg0KLyogTGlzdHMgKi8NCnVsLCBvbCB7DQogIHBhZGRpbmctbGVmdDogMS41ZW07DQogIG1hcmdpbi1ib3R0b206IDEuNWVtOw0KfQ0KDQpsaSB7DQogIG1hcmdpbi1ib3R0b206IDAuNWVtOw0KfQ0KDQovKiBCbG9ja3F1b3RlcyAqLw0KYmxvY2txdW90ZSB7DQogIGJvcmRlci1sZWZ0OiA0cHggc29saWQgIzAwN2JmZjsNCiAgcGFkZGluZy1sZWZ0OiAxZW07DQogIG1hcmdpbjogMWVtIDA7DQogIGNvbG9yOiAjNmM3NTdkOw0KfQ0KDQovKiBUYWJsZXMgKi8NCnRhYmxlIHsNCiAgd2lkdGg6IDEwMCU7DQogIG1hcmdpbjogMmVtIDA7DQogIGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7DQp9DQoNCnRoLCB0ZCB7DQogIHBhZGRpbmc6IDEycHg7DQogIGJvcmRlcjogMXB4IHNvbGlkICNlOWVjZWY7DQp9DQoNCnRoIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYTsNCiAgZm9udC13ZWlnaHQ6IDYwMDsNCn0NCg0KLyogTGlua3MgKi8NCmEgew0KICBjb2xvcjogIzAwN2JmZjsNCiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOw0KfQ0KDQphOmhvdmVyIHsNCiAgY29sb3I6ICMwMDU2YjM7DQogIHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOw0KfQ0KDQovKiBTZWN0aW9uIHNwYWNpbmcgKi8NCnNlY3Rpb24gew0KICBtYXJnaW4tYm90dG9tOiAzZW07DQp9DQoNCi8qIENvZGUgb3V0cHV0ICovDQoub3V0cHV0IHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYTsNCiAgYm9yZGVyOiAxcHggc29saWQgI2U5ZWNlZjsNCiAgYm9yZGVyLXJhZGl1czogNHB4Ow0KICBwYWRkaW5nOiAxZW07DQogIG1hcmdpbi10b3A6IDFlbTsNCn0NCg0KLyogV2FybmluZyBhbmQgbm90ZSBib3hlcyAqLw0KLm5vdGUsIC53YXJuaW5nIHsNCiAgcGFkZGluZzogMWVtOw0KICBtYXJnaW46IDFlbSAwOw0KICBib3JkZXItcmFkaXVzOiA0cHg7DQp9DQoNCi5ub3RlIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2UzZjJmZDsNCiAgYm9yZGVyOiAxcHggc29saWQgIzkwY2FmOTsNCn0NCg0KLndhcm5pbmcgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmM2UwOw0KICBib3JkZXI6IDFweCBzb2xpZCAjZmZiNzRkOw0KfQ0KDQpgYGANCg0KIyBTZXR1cCB0aGUgU2V1cmF0IE9iamVjdA0KVGhpcyB0dXJvcmlhbCBpcyBtb2RpZmllZCBmcm9tIFtTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvcGJtYzNrX3R1dG9yaWFsKS4NCkZvciB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIGJlIGFuYWx5emluZyBhIGRhdGFzZXQgb2YgUGVyaXBoZXJhbCBCbG9vZCBNb25vbnVjbGVhciBDZWxscyAoUEJNQykgZnJlZWx5IGF2YWlsYWJsZSBmcm9tIDEwWCBHZW5vbWljcy4gVGhlcmUgYXJlIDIsNzAwIHNpbmdsZSBjZWxscyB0aGF0IHdlcmUgc2VxdWVuY2VkIG9uIHRoZSBJbGx1bWluYSBOZXh0U2VxIDUwMC4gVGhlIHJhdyBkYXRhIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9jZi4xMHhnZW5vbWljcy5jb20vc2FtcGxlcy9jZWxsL3BibWMzay9wYm1jM2tfZmlsdGVyZWRfZ2VuZV9iY19tYXRyaWNlcy50YXIuZ3opLg0KDQpXZSBzdGFydCBieSByZWFkaW5nIGluIHRoZSBkYXRhLiBUaGUgYFJlYWQxMFgoKWAgZnVuY3Rpb24gcmVhZHMgaW4gdGhlIG91dHB1dCBvZiB0aGUgW2NlbGxyYW5nZXJdKGh0dHBzOi8vc3VwcG9ydC4xMHhnZW5vbWljcy5jb20vc2luZ2xlLWNlbGwtZ2VuZS1leHByZXNzaW9uL3NvZnR3YXJlL3BpcGVsaW5lcy9sYXRlc3Qvd2hhdC1pcy1jZWxsLXJhbmdlcikgcGlwZWxpbmUgZnJvbSAxMFgsIHJldHVybmluZyBhIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllZCAoVU1JKSBjb3VudCBtYXRyaXguIFRoZSB2YWx1ZXMgaW4gdGhpcyBtYXRyaXggcmVwcmVzZW50IHRoZSBudW1iZXIgb2YgbW9sZWN1bGVzIGZvciBlYWNoIGZlYXR1cmUgKGkuZS4gZ2VuZTsgcm93KSB0aGF0IGFyZSBkZXRlY3RlZCBpbiBlYWNoIGNlbGwgKGNvbHVtbikuIE5vdGUgdGhhdCBtb3JlIHJlY2VudCB2ZXJzaW9ucyBvZiBjZWxscmFuZ2VyIG5vdyBhbHNvIG91dHB1dCB1c2luZyB0aGUgW2g1IGZpbGUgZm9ybWF0XShodHRwczovL3N1cHBvcnQuMTB4Z2Vub21pY3MuY29tL3NpbmdsZS1jZWxsLWdlbmUtZXhwcmVzc2lvbi9zb2Z0d2FyZS9waXBlbGluZXMvbGF0ZXN0L2FkdmFuY2VkL2g1X21hdHJpY2VzKSwgd2hpY2ggY2FuIGJlIHJlYWQgaW4gdXNpbmcgdGhlIGBSZWFkMTBYX2g1KClgIGZ1bmN0aW9uIGluIFNldXJhdC4NCg0KV2UgbmV4dCB1c2UgdGhlIGNvdW50IG1hdHJpeCB0byBjcmVhdGUgYSBgU2V1cmF0YCBvYmplY3QuIFRoZSBvYmplY3Qgc2VydmVzIGFzIGEgY29udGFpbmVyIHRoYXQgY29udGFpbnMgYm90aCBkYXRhIChsaWtlIHRoZSBjb3VudCBtYXRyaXgpIGFuZCBhbmFseXNpcyAobGlrZSBQQ0EsIG9yIGNsdXN0ZXJpbmcgcmVzdWx0cykgZm9yIGEgc2luZ2xlLWNlbGwgZGF0YXNldC4gRm9yIG1vcmUgaW5mb3JtYXRpb24sIGNoZWNrIG91dCBvdXIgW1NldXJhdCBvYmplY3QgaW50ZXJhY3Rpb24gdmlnbmV0dGVdLCBvciBvdXIgW0dpdEh1YiBXaWtpXShodHRwczovL2dpdGh1Yi5jb20vc2F0aWphbGFiL3NldXJhdC93aWtpKS4gRm9yIGV4YW1wbGUsIGluIFNldXJhdCB2NSwgdGhlIGNvdW50IG1hdHJpeCBpcyBzdG9yZWQgaW4gYHBibWNbWyJSTkEiXV0kY291bnRzYC4NCg0KYGBge3IgaW5pdH0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KFNldXJhdCkNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KDQojIExvYWQgdGhlIFBCTUMgZGF0YXNldA0KcGJtYy5kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSAiZGF0YS9oZzE5LyIpDQojIEluaXRpYWxpemUgdGhlIFNldXJhdCBvYmplY3Qgd2l0aCB0aGUgcmF3IChub24tbm9ybWFsaXplZCBkYXRhKS4NCnBibWMgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IHBibWMuZGF0YSwgcHJvamVjdCA9ICJwYm1jM2siLCBtaW4uY2VsbHMgPSAzLCBtaW4uZmVhdHVyZXMgPSAyMDApDQpwYm1jDQpgYGANCjxkZXRhaWxzPg0KICA8c3VtbWFyeT4qKldoYXQgZG9lcyBkYXRhIGluIGEgY291bnQgbWF0cml4IGxvb2sgbGlrZT8qKjwvc3VtbWFyeT4NCg0KYGBge3J9DQojIExldHMgZXhhbWluZSBhIGZldyBnZW5lcyBpbiB0aGUgZmlyc3QgdGhpcnR5IGNlbGxzDQpwYm1jLmRhdGFbYygiQ0QzRCIsIlRDTDFBIiwiTVM0QTEiKSwgMTozMF0NCmBgYA0KDQpUaGUgYC5gIHZhbHVlcyBpbiB0aGUgbWF0cml4IHJlcHJlc2VudCAwcyAobm8gbW9sZWN1bGVzIGRldGVjdGVkKS4gU2luY2UgbW9zdCB2YWx1ZXMgaW4gYW4gc2NSTkEtc2VxIG1hdHJpeCBhcmUgMCwgIFNldXJhdCB1c2VzIGEgc3BhcnNlLW1hdHJpeCByZXByZXNlbnRhdGlvbiB3aGVuZXZlciBwb3NzaWJsZS4gVGhpcyByZXN1bHRzIGluIHNpZ25pZmljYW50IG1lbW9yeSBhbmQgc3BlZWQgc2F2aW5ncyBmb3IgRHJvcC1zZXEvaW5Ecm9wLzEweCBkYXRhLg0KDQpgYGB7cn0NCmRlbnNlLnNpemUgPC0gb2JqZWN0LnNpemUoYXMubWF0cml4KHBibWMuZGF0YSkpDQpkZW5zZS5zaXplDQpzcGFyc2Uuc2l6ZSA8LSBvYmplY3Quc2l6ZShwYm1jLmRhdGEpDQpzcGFyc2Uuc2l6ZQ0KZGVuc2Uuc2l6ZSAvIHNwYXJzZS5zaXplDQpgYGANCjwvZGV0YWlscz4NClwgIA0KDQojIFN0YW5kYXJkIHByZS1wcm9jZXNzaW5nIHdvcmtmbG93DQoNClRoZSBzdGVwcyBiZWxvdyBlbmNvbXBhc3MgdGhlIHN0YW5kYXJkIHByZS1wcm9jZXNzaW5nIHdvcmtmbG93IGZvciBzY1JOQS1zZXEgZGF0YSBpbiBTZXVyYXQuIFRoZXNlIHJlcHJlc2VudCB0aGUgc2VsZWN0aW9uIGFuZCBmaWx0cmF0aW9uIG9mIGNlbGxzIGJhc2VkIG9uIFFDIG1ldHJpY3MsIGRhdGEgbm9ybWFsaXphdGlvbiBhbmQgc2NhbGluZywgYW5kIHRoZSBkZXRlY3Rpb24gb2YgaGlnaGx5IHZhcmlhYmxlIGZlYXR1cmVzLg0KDQojIyBRQyBhbmQgc2VsZWN0aW5nIGNlbGxzIGZvciBmdXJ0aGVyIGFuYWx5c2lzDQoNClNldXJhdCBhbGxvd3MgeW91IHRvIGVhc2lseSBleHBsb3JlIFFDIG1ldHJpY3MgYW5kIGZpbHRlciBjZWxscyBiYXNlZCBvbiBhbnkgdXNlci1kZWZpbmVkIGNyaXRlcmlhLiBBIGZldyBRQyBtZXRyaWNzIFtjb21tb25seSB1c2VkXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM0NzU4MTAzLykgYnkgdGhlIGNvbW11bml0eSBpbmNsdWRlDQoNCiogVGhlIG51bWJlciBvZiB1bmlxdWUgZ2VuZXMgZGV0ZWN0ZWQgaW4gZWFjaCBjZWxsLiANCiAgICArIExvdy1xdWFsaXR5IGNlbGxzIG9yIGVtcHR5IGRyb3BsZXRzIHdpbGwgb2Z0ZW4gaGF2ZSB2ZXJ5IGZldyBnZW5lcw0KICAgICsgQ2VsbCBkb3VibGV0cyBvciBtdWx0aXBsZXRzIG1heSBleGhpYml0IGFuIGFiZXJyYW50bHkgaGlnaCBnZW5lIGNvdW50DQoqIFNpbWlsYXJseSwgdGhlIHRvdGFsIG51bWJlciBvZiBtb2xlY3VsZXMgZGV0ZWN0ZWQgd2l0aGluIGEgY2VsbCAoY29ycmVsYXRlcyBzdHJvbmdseSB3aXRoIHVuaXF1ZSBnZW5lcykNCiogVGhlIHBlcmNlbnRhZ2Ugb2YgcmVhZHMgdGhhdCBtYXAgdG8gdGhlIG1pdG9jaG9uZHJpYWwgZ2Vub21lDQogICAgKyBMb3ctcXVhbGl0eSAvIGR5aW5nIGNlbGxzIG9mdGVuIGV4aGliaXQgZXh0ZW5zaXZlIG1pdG9jaG9uZHJpYWwgY29udGFtaW5hdGlvbg0KICAgICsgV2UgY2FsY3VsYXRlIG1pdG9jaG9uZHJpYWwgUUMgbWV0cmljcyB3aXRoIHRoZSBgUGVyY2VudGFnZUZlYXR1cmVTZXQoKWAgZnVuY3Rpb24sIHdoaWNoIGNhbGN1bGF0ZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY291bnRzIG9yaWdpbmF0aW5nIGZyb20gYSBzZXQgb2YgZmVhdHVyZXMNCiAgICArIFdlIHVzZSB0aGUgc2V0IG9mIGFsbCBnZW5lcyBzdGFydGluZyB3aXRoIGBNVC1gIGFzIGEgc2V0IG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMNCg0KYGBge3IgbWl0b30NCiMgVGhlIFtbIG9wZXJhdG9yIGNhbiBhZGQgY29sdW1ucyB0byBvYmplY3QgbWV0YWRhdGEuIFRoaXMgaXMgYSBncmVhdCBwbGFjZSB0byBzdGFzaCBRQyBzdGF0cw0KcGJtY1tbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocGJtYywgcGF0dGVybiA9ICJeTVQtIikNCmBgYA0KDQo8ZGV0YWlscz4NCiAgPHN1bW1hcnk+KipXaGVyZSBhcmUgUUMgbWV0cmljcyBzdG9yZWQgaW4gU2V1cmF0PyoqPC9zdW1tYXJ5Pg0KDQoqIFRoZSBudW1iZXIgb2YgdW5pcXVlIGdlbmVzIGFuZCB0b3RhbCBtb2xlY3VsZXMgYXJlIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlZCBkdXJpbmcgYENyZWF0ZVNldXJhdE9iamVjdCgpYA0KICAgICsgWW91IGNhbiBmaW5kIHRoZW0gc3RvcmVkIGluIHRoZSBvYmplY3QgbWV0YSBkYXRhDQpgYGB7ciBxY30NCiMgU2hvdyBRQyBtZXRyaWNzIGZvciB0aGUgZmlyc3QgNSBjZWxscw0KaGVhZChwYm1jQG1ldGEuZGF0YSwgNSkNCmBgYA0KPC9kZXRhaWxzPg0KXCAgDQoNCkluIHRoZSBleGFtcGxlIGJlbG93LCB3ZSB2aXN1YWxpemUgUUMgbWV0cmljcywgYW5kIHVzZSB0aGVzZSB0byBmaWx0ZXIgY2VsbHMuDQoNCiogV2UgZmlsdGVyIGNlbGxzIHRoYXQgaGF2ZSB1bmlxdWUgZmVhdHVyZSBjb3VudHMgb3ZlciAyLDUwMCBvciBsZXNzIHRoYW4gMjAwDQoqIFdlIGZpbHRlciBjZWxscyB0aGF0IGhhdmUgPjUlIG1pdG9jaG9uZHJpYWwgY291bnRzDQogICAgDQpgYGB7ciBxYzIsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEzfQ0KDQojVmlzdWFsaXplIFFDIG1ldHJpY3MgYXMgYSB2aW9saW4gcGxvdA0KVmxuUGxvdChwYm1jLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpDQoNCiMgRmVhdHVyZVNjYXR0ZXIgaXMgdHlwaWNhbGx5IHVzZWQgdG8gdmlzdWFsaXplIGZlYXR1cmUtZmVhdHVyZSByZWxhdGlvbnNoaXBzLCBidXQgY2FuIGJlIHVzZWQgZm9yIGFueXRoaW5nIGNhbGN1bGF0ZWQgYnkgdGhlIG9iamVjdCwgaS5lLiBjb2x1bW5zIGluIG9iamVjdCBtZXRhZGF0YSwgUEMgc2NvcmVzIGV0Yy4NCg0KcGxvdDEgPC0gRmVhdHVyZVNjYXR0ZXIocGJtYywgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIGZlYXR1cmUyID0gInBlcmNlbnQubXQiKSANCnBsb3QyIDwtIEZlYXR1cmVTY2F0dGVyKHBibWMsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiKSANCnBsb3QxICsgcGxvdDINCg0KcGJtYyA8LSBzdWJzZXQocGJtYywgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMjAwICYgbkZlYXR1cmVfUk5BIDwgMjUwMCAmIHBlcmNlbnQubXQgPCA1KQ0KYGBgDQoNCioqKg0KDQojIE5vcm1hbGl6aW5nIHRoZSBkYXRhDQoNCkFmdGVyIHJlbW92aW5nIHVud2FudGVkIGNlbGxzIGZyb20gdGhlIGRhdGFzZXQsIHRoZSBuZXh0IHN0ZXAgaXMgdG8gbm9ybWFsaXplIHRoZSBkYXRhLiBCeSBkZWZhdWx0LCB3ZSBlbXBsb3kgYSBnbG9iYWwtc2NhbGluZyBub3JtYWxpemF0aW9uIG1ldGhvZCAiTG9nTm9ybWFsaXplIiB0aGF0IG5vcm1hbGl6ZXMgdGhlIGZlYXR1cmUgZXhwcmVzc2lvbiBtZWFzdXJlbWVudHMgZm9yIGVhY2ggY2VsbCBieSB0aGUgdG90YWwgZXhwcmVzc2lvbiwgbXVsdGlwbGllcyB0aGlzIGJ5IGEgc2NhbGUgZmFjdG9yICgxMCwwMDAgYnkgZGVmYXVsdCksIGFuZCBsb2ctdHJhbnNmb3JtcyB0aGUgcmVzdWx0LiBJbiBTZXVyYXQgdjUsIE5vcm1hbGl6ZWQgdmFsdWVzIGFyZSBzdG9yZWQgaW4gYHBibWNbWyJSTkEiXV0kZGF0YWAuDQoNCg0KDQpgYGB7ciBub3JtYWxpemV9DQpwYm1jIDwtIE5vcm1hbGl6ZURhdGEocGJtYywgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMWU0KQ0KYGBgDQpGb3IgY2xhcml0eSwgaW4gdGhpcyBwcmV2aW91cyBsaW5lIG9mIGNvZGUgKGFuZCBpbiBmdXR1cmUgY29tbWFuZHMpLCB3ZSBwcm92aWRlIHRoZSBkZWZhdWx0IHZhbHVlcyBmb3IgY2VydGFpbiBwYXJhbWV0ZXJzIGluIHRoZSBmdW5jdGlvbiBjYWxsLiBIb3dldmVyLCB0aGlzIGlzbid0IHJlcXVpcmVkIGFuZCB0aGUgc2FtZSBiZWhhdmlvciBjYW4gYmUgYWNoaWV2ZWQgd2l0aDoNCg0KYGBge3Igbm9ybWFsaXplLmRlZmF1bHQsIGV2YWwgPSBGQUxTRX0NCnBibWMgPC0gTm9ybWFsaXplRGF0YShwYm1jKQ0KYGBgDQoNCldoaWxlIHRoaXMgbWV0aG9kIG9mIG5vcm1hbGl6YXRpb24gaXMgc3RhbmRhcmQgYW5kIHdpZGVseSB1c2VkIGluIHNjUk5BLXNlcSBhbmFseXNpcywgZ2xvYmFsLXNjYWxpbmcgcmVsaWVzIG9uIGFuIGFzc3VtcHRpb24gdGhhdCBlYWNoIGNlbGwgb3JpZ2luYWxseSBjb250YWlucyB0aGUgc2FtZSBudW1iZXIgb2YgUk5BIG1vbGVjdWxlcy4gV2UgYW5kIG90aGVycyBoYXZlIGRldmVsb3BlZCBhbHRlcm5hdGl2ZSB3b3JrZmxvd3MgZm9yIHRoZSBzaW5nbGUgY2VsbCBwcmVwcm9jZXNzaW5nIHRoYXQgZG8gbm90IG1ha2UgdGhlc2UgYXNzdW1wdGlvbnMuIEZvciB1c2VycyB3aG8gYXJlIGludGVyZXN0ZWQsIHBsZWFzZSBjaGVjayBvdXQgb3VyIGBTQ1RyYW5zZm9ybSgpYCBub3JtYWxpemF0aW9uIHdvcmtmbG93LiBUaGUgbWV0aG9kIGlzIGRlc2NyaWJlZCBpbiBvdXJbcGFwZXJdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMjEtMDI1ODQtOSksIHdpdGggYSBzZXBhcmF0ZSB2aWduZXR0ZSB1c2luZyBTZXVyYXQgW2hlcmVdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvc2N0cmFuc2Zvcm1fdmlnbmV0dGUuaHRtbCkuIFRoZSB1c2Ugb2YgYFNDVHJhbnNmb3JtYCByZXBsYWNlcyB0aGUgbmVlZCB0byBydW4gYE5vcm1hbGl6ZURhdGFgLCBgRmluZFZhcmlhYmxlRmVhdHVyZXNgLCBvciBgU2NhbGVEYXRhYCAoZGVzY3JpYmVkIGJlbG93LikNCg0KDQojIElkZW50aWZpY2F0aW9uIG9mIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyAoZmVhdHVyZSBzZWxlY3Rpb24pDQoNCldlIG5leHQgY2FsY3VsYXRlIGEgc3Vic2V0IG9mIGZlYXR1cmVzIHRoYXQgZXhoaWJpdCBoaWdoIGNlbGwtdG8tY2VsbCB2YXJpYXRpb24gaW4gdGhlIGRhdGFzZXQgKGkuZSwgdGhleSBhcmUgaGlnaGx5IGV4cHJlc3NlZCBpbiBzb21lIGNlbGxzLCBhbmQgbG93bHkgZXhwcmVzc2VkIGluIG90aGVycykuIFdlIGFuZCBbb3RoZXJzXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI2NDUpIGhhdmUgZm91bmQgdGhhdCBmb2N1c2luZyBvbiB0aGVzZSBnZW5lcyBpbiBkb3duc3RyZWFtIGFuYWx5c2lzIGhlbHBzIHRvIGhpZ2hsaWdodCBiaW9sb2dpY2FsIHNpZ25hbCBpbiBzaW5nbGUtY2VsbCBkYXRhc2V0cy4NCg0KT3VyIHByb2NlZHVyZSBpbiBTZXVyYXQgaXMgZGVzY3JpYmVkIGluIGRldGFpbCBbaGVyZV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxsLjIwMTkuMDUuMDMxKSwgYW5kIGltcHJvdmVzIG9uIHByZXZpb3VzIHZlcnNpb25zIGJ5IGRpcmVjdGx5IG1vZGVsaW5nIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcCBpbmhlcmVudCBpbiBzaW5nbGUtY2VsbCBkYXRhLCBhbmQgaXMgaW1wbGVtZW50ZWQgaW4gdGhlIGBGaW5kVmFyaWFibGVGZWF0dXJlcygpYCBmdW5jdGlvbi4gQnkgZGVmYXVsdCwgd2UgcmV0dXJuIDIsMDAwIGZlYXR1cmVzIHBlciBkYXRhc2V0LiBUaGVzZSB3aWxsIGJlIHVzZWQgaW4gZG93bnN0cmVhbSBhbmFseXNpcywgbGlrZSBQQ0EuDQoNCmBgYHtyIHZhcl9mZWF0dXJlcywgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9DQpwYm1jIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHBibWMsIHNlbGVjdGlvbi5tZXRob2QgPSAndnN0JywgbmZlYXR1cmVzID0gMjAwMCkNCg0KIyBJZGVudGlmeSB0aGUgMTAgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMNCnRvcDEwIDwtIGhlYWQoVmFyaWFibGVGZWF0dXJlcyhwYm1jKSwgMTApDQoNCiMgcGxvdCB2YXJpYWJsZSBmZWF0dXJlcyB3aXRoIGFuZCB3aXRob3V0IGxhYmVscw0KcGxvdDEgPC0gVmFyaWFibGVGZWF0dXJlUGxvdChwYm1jKQ0KcGxvdDIgPC0gTGFiZWxQb2ludHMocGxvdCA9IHBsb3QxLCBwb2ludHMgPSB0b3AxMCwgcmVwZWwgPSBUUlVFKQ0KcGxvdDEgKyBwbG90Mg0KYGBgDQoNCioqKg0KDQojIFNjYWxpbmcgdGhlIGRhdGENCg0KTmV4dCwgd2UgYXBwbHkgYSBsaW5lYXIgdHJhbnNmb3JtYXRpb24gKCdzY2FsaW5nJykgdGhhdCBpcyBhIHN0YW5kYXJkIHByZS1wcm9jZXNzaW5nIHN0ZXAgcHJpb3IgdG8gZGltZW5zaW9uYWwgcmVkdWN0aW9uIHRlY2huaXF1ZXMgbGlrZSBQQ0EuIFRoZSBgU2NhbGVEYXRhKClgIGZ1bmN0aW9uOg0KDQoqIFNoaWZ0cyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUsIHNvIHRoYXQgdGhlIG1lYW4gZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbHMgaXMgMA0KKiBTY2FsZXMgdGhlIGV4cHJlc3Npb24gb2YgZWFjaCBnZW5lLCBzbyB0aGF0IHRoZSB2YXJpYW5jZSBhY3Jvc3MgY2VsbHMgaXMgMQ0KICAgICsgVGhpcyBzdGVwIGdpdmVzIGVxdWFsIHdlaWdodCBpbiBkb3duc3RyZWFtIGFuYWx5c2VzLCBzbyB0aGF0IGhpZ2hseS1leHByZXNzZWQgZ2VuZXMgZG8gbm90IGRvbWluYXRlDQoqIFRoZSByZXN1bHRzIG9mIHRoaXMgYXJlIHN0b3JlZCBpbiBgcGJtY1tbIlJOQSJdXSRzY2FsZS5kYXRhYA0KKiBCeSBkZWZhdWx0LCBvbmx5IHZhcmlhYmxlIGZlYXR1cmVzIGFyZSBzY2FsZWQuIA0KKiBZb3UgY2FuIHNwZWNpZnkgdGhlIGBmZWF0dXJlc2AgYXJndW1lbnQgdG8gc2NhbGUgYWRkaXRpb25hbCBmZWF0dXJlcw0KDQpgYGB7ciByZWdyZXNzLCByZXN1bHRzPSdoaWRlJ30NCmFsbC5nZW5lcyA8LSByb3duYW1lcyhwYm1jKQ0KcGJtYyA8LSBTY2FsZURhdGEocGJtYywgZmVhdHVyZXMgPSBhbGwuZ2VuZXMpDQpgYGANCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PioqSG93IGNhbiBJIHJlbW92ZSB1bndhbnRlZCBzb3VyY2VzIG9mIHZhcmlhdGlvbioqPC9zdW1tYXJ5Pg0KDQpJbiBTZXVyYXQsIHdlIGFsc28gdXNlIHRoZSBgU2NhbGVEYXRhKClgIGZ1bmN0aW9uIHRvIHJlbW92ZSB1bndhbnRlZCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBmcm9tIGEgc2luZ2xlLWNlbGwgZGF0YXNldC4gRm9yIGV4YW1wbGUsIHdlIGNvdWxkICdyZWdyZXNzIG91dCcgaGV0ZXJvZ2VuZWl0eSBhc3NvY2lhdGVkIHdpdGggKGZvciBleGFtcGxlKSBbY2VsbCBjeWNsZSBzdGFnZV0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9hcnRpY2xlcy9jZWxsX2N5Y2xlX3ZpZ25ldHRlKSwgb3IgbWl0b2Nob25kcmlhbCBjb250YW1pbmF0aW9uIGkuZS46IA0KYGBge3IgcmVncmVzc3Zhcm10LCBldmFsID0gRkFMU0V9DQpwYm1jIDwtIFNjYWxlRGF0YShwYm1jLCB2YXJzLnRvLnJlZ3Jlc3MgPSAncGVyY2VudC5tdCcpDQpgYGANCkhvd2V2ZXIsIHBhcnRpY3VsYXJseSBmb3IgYWR2YW5jZWQgdXNlcnMgd2hvIHdvdWxkIGxpa2UgdG8gdXNlIHRoaXMgZnVuY3Rpb25hbGl0eSwgd2Ugc3Ryb25nbHkgcmVjb21tZW5kIHRoZSB1c2Ugb2Ygb3VyIG5ldyBub3JtYWxpemF0aW9uIHdvcmtmbG93LCBgU0NUcmFuc2Zvcm0oKWAuIFRoZSBtZXRob2QgaXMgZGVzY3JpYmVkIGluIG91ciBbcGFwZXJdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMjEtMDI1ODQtOSksIHdpdGggYSBzZXBhcmF0ZSB2aWduZXR0ZSB1c2luZyBTZXVyYXQgW2hlcmVdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvc2N0cmFuc2Zvcm1fdmlnbmV0dGUpLiBBcyB3aXRoIGBTY2FsZURhdGEoKWAsIHRoZSBmdW5jdGlvbiBgU0NUcmFuc2Zvcm0oKWAgYWxzbyBpbmNsdWRlcyBhIGB2YXJzLnRvLnJlZ3Jlc3NgIHBhcmFtZXRlci4NCjwvZGV0YWlscz4NCg0KKioqDQoNCiMgUGVyZm9ybSBsaW5lYXIgZGltZW5zaW9uYWwgcmVkdWN0aW9uDQoNCk5leHQgd2UgcGVyZm9ybSBQQ0Egb24gdGhlIHNjYWxlZCBkYXRhLiBCeSBkZWZhdWx0LCBvbmx5IHRoZSBwcmV2aW91c2x5IGRldGVybWluZWQgdmFyaWFibGUgZmVhdHVyZXMgYXJlIHVzZWQgYXMgaW5wdXQsIGJ1dCBjYW4gYmUgZGVmaW5lZCB1c2luZyBgZmVhdHVyZXNgIGFyZ3VtZW50IGlmIHlvdSB3aXNoIHRvIGNob29zZSBhIGRpZmZlcmVudCBzdWJzZXQgKGlmIHlvdSBkbyB3YW50IHRvIHVzZSBhIGN1c3RvbSBzdWJzZXQgb2YgZmVhdHVyZXMsIG1ha2Ugc3VyZSB5b3UgcGFzcyB0aGVzZSB0byBgU2NhbGVEYXRhYCBmaXJzdCkuDQoNCkZvciB0aGUgZmlyc3QgcHJpbmNpcGFsIGNvbXBvbmVudHMsIFNldXJhdCBvdXRwdXRzIGEgbGlzdCBvZiBnZW5lcyB3aXRoIHRoZSBtb3N0IHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBsb2FkaW5ncywgcmVwcmVzZW50aW5nIG1vZHVsZXMgb2YgZ2VuZXMgdGhhdCBleGhpYml0IGVpdGhlciBjb3JyZWxhdGlvbiAob3IgYW50aS1jb3JyZWxhdGlvbikgYWNyb3NzIHNpbmdsZS1jZWxscyBpbiB0aGUgZGF0YXNldC4NCg0KYGBge3IgcGNhLHJlc3VsdHM9J2hpZGUnfQ0KcGJtYyA8LSBSdW5QQ0EocGJtYywgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHBibWMpKQ0KYGBgDQoNClNldXJhdCBwcm92aWRlcyBzZXZlcmFsIHVzZWZ1bCB3YXlzIG9mIHZpc3VhbGl6aW5nIGJvdGggY2VsbHMgYW5kIGZlYXR1cmVzIHRoYXQgZGVmaW5lIHRoZSBQQ0EsIGluY2x1ZGluZyBgVml6RGltUmVkdWN0aW9uKClgLCBgRGltUGxvdCgpYCwgYW5kIGBEaW1IZWF0bWFwKClgDQoNCmBgYHtyIHBjYV92aXosIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPVRSVUV9DQojIEV4YW1pbmUgYW5kIHZpc3VhbGl6ZSBQQ0EgcmVzdWx0cyBhIGZldyBkaWZmZXJlbnQgd2F5cw0KcHJpbnQocGJtY1tbJ3BjYSddXSwgZGltcyA9IDE6NSwgbmZlYXR1cmVzID0gNSkNClZpekRpbUxvYWRpbmdzKHBibWMsIGRpbXMgPSAxOjIsIHJlZHVjdGlvbiA9ICdwY2EnKQ0KRGltUGxvdChwYm1jLCByZWR1Y3Rpb24gPSAncGNhJykgKyBOb0xlZ2VuZCgpDQpgYGANCg0KSW4gcGFydGljdWxhciBgRGltSGVhdG1hcCgpYCBhbGxvd3MgZm9yIGVhc3kgZXhwbG9yYXRpb24gb2YgdGhlIHByaW1hcnkgc291cmNlcyBvZiBoZXRlcm9nZW5laXR5IGluIGEgZGF0YXNldCwgYW5kIGNhbiBiZSB1c2VmdWwgd2hlbiB0cnlpbmcgdG8gZGVjaWRlIHdoaWNoIFBDcyB0byBpbmNsdWRlIGZvciBmdXJ0aGVyIGRvd25zdHJlYW0gYW5hbHlzZXMuIEJvdGggY2VsbHMgYW5kIGZlYXR1cmVzIGFyZSBvcmRlcmVkIGFjY29yZGluZyB0byB0aGVpciBQQ0Egc2NvcmVzLiBTZXR0aW5nIGBjZWxsc2AgdG8gYSBudW1iZXIgcGxvdHMgdGhlICdleHRyZW1lJyBjZWxscyBvbiBib3RoIGVuZHMgb2YgdGhlIHNwZWN0cnVtLCB3aGljaCBkcmFtYXRpY2FsbHkgc3BlZWRzIHBsb3R0aW5nIGZvciBsYXJnZSBkYXRhc2V0cy4gVGhvdWdoIGNsZWFybHkgYSBzdXBlcnZpc2VkIGFuYWx5c2lzLCB3ZSBmaW5kIHRoaXMgdG8gYmUgYSB2YWx1YWJsZSB0b29sIGZvciBleHBsb3JpbmcgY29ycmVsYXRlZCBmZWF0dXJlIHNldHMuDQoNCmBgYHtyIHNpbmdsZS1oZWF0bWFwLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD05fQ0KRGltSGVhdG1hcChwYm1jLCBkaW1zID0gMSwgY2VsbHMgPSA1MDAsIGJhbGFuY2VkID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBtdWx0aS1oZWF0bWFwLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9OX0NCkRpbUhlYXRtYXAocGJtYywgZGltcyA9IDE6MTUsIGNlbGxzID0gNTAwLCBiYWxhbmNlZCA9IFRSVUUpDQpgYGANCg0KIyBEZXRlcm1pbmUgdGhlICdkaW1lbnNpb25hbGl0eScgb2YgdGhlIGRhdGFzZXQNCg0KVG8gb3ZlcmNvbWUgdGhlIGV4dGVuc2l2ZSB0ZWNobmljYWwgbm9pc2UgaW4gYW55IHNpbmdsZSBmZWF0dXJlIGZvciBzY1JOQS1zZXEgZGF0YSwgU2V1cmF0IGNsdXN0ZXJzIGNlbGxzIGJhc2VkIG9uIHRoZWlyIFBDQSBzY29yZXMsIHdpdGggZWFjaCBQQyBlc3NlbnRpYWxseSByZXByZXNlbnRpbmcgYSAnbWV0YWZlYXR1cmUnIHRoYXQgY29tYmluZXMgaW5mb3JtYXRpb24gYWNyb3NzIGEgY29ycmVsYXRlZCBmZWF0dXJlIHNldC4gVGhlIHRvcCBwcmluY2lwYWwgY29tcG9uZW50cyB0aGVyZWZvcmUgcmVwcmVzZW50IGEgcm9idXN0IGNvbXByZXNzaW9uIG9mIHRoZSBkYXRhc2V0LiBIb3dldmVyLCBob3cgbWFueSBjb21wb25lbnRzIHNob3VsZCB3ZSBjaG9vc2UgdG8gaW5jbHVkZT8gMTA/IDIwPyAxMDA/DQoNCkluIFtNYWNvc2tvICpldCBhbCpdKGh0dHA6Ly93d3cuY2VsbC5jb20vYWJzdHJhY3QvUzAwOTItODY3NCgxNSkwMDU0OS04KSwgd2UgaW1wbGVtZW50ZWQgYSByZXNhbXBsaW5nIHRlc3QgaW5zcGlyZWQgYnkgdGhlIEphY2tTdHJhdyBwcm9jZWR1cmUuIFdoaWxlIHN0aWxsIGF2YWlsYWJsZSBpbiBTZXVyYXQgWyhzZWUgcHJldmlvdXMgdmlnbmV0dGUpXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbCksIHRoaXMgaXMgYSBzbG93IGFuZCBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIHByb2NlZHVyZSwgYW5kIHdlIGlzIG5vIGxvbmdlciByb3V0aW5lbHkgdXNlZCBpbiBzaW5nbGUgY2VsbCBhbmFseXNpcy4NCg0KDQpBbiBhbHRlcm5hdGl2ZSBoZXVyaXN0aWMgbWV0aG9kIGdlbmVyYXRlcyBhbiAnRWxib3cgcGxvdCc6IGEgcmFua2luZyBvZiBwcmluY2lwbGUgY29tcG9uZW50cyBiYXNlZCBvbiB0aGUgcGVyY2VudGFnZSBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBvbmUgKGBFbGJvd1Bsb3QoKWAgZnVuY3Rpb24pLiBJbiB0aGlzIGV4YW1wbGUsIHdlIGNhbiBvYnNlcnZlIGFuICdlbGJvdycgYXJvdW5kIFBDOS0xMCwgc3VnZ2VzdGluZyB0aGF0IHRoZSBtYWpvcml0eSBvZiB0cnVlIHNpZ25hbCBpcyBjYXB0dXJlZCBpbiB0aGUgZmlyc3QgMTAgUENzLiANCg0KYGBge3IgZWxib3dfcGxvdCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9DQpFbGJvd1Bsb3QocGJtYykNCmBgYA0KDQpJZGVudGlmeWluZyB0aGUgdHJ1ZSBkaW1lbnNpb25hbGl0eSBvZiBhIGRhdGFzZXQgLS0gY2FuIGJlIGNoYWxsZW5naW5nL3VuY2VydGFpbiBmb3IgdGhlIHVzZXIuIFdlIHRoZXJlZm9yZSBzdWdnZXN0IHRoZXNlIG11bHRpcGxlIGFwcHJvYWNoZXMgZm9yIHVzZXJzLiBUaGUgZmlyc3QgaXMgbW9yZSBzdXBlcnZpc2VkLCBleHBsb3JpbmcgUENzIHRvIGRldGVybWluZSByZWxldmFudCBzb3VyY2VzIG9mIGhldGVyb2dlbmVpdHksIGFuZCBjb3VsZCBiZSB1c2VkIGluIGNvbmp1bmN0aW9uIHdpdGggR1NFQSBmb3IgZXhhbXBsZS4gVGhlIHNlY29uZCAoYEVsYm93UGxvdGApIFRoZSB0aGlyZCBpcyBhIGhldXJpc3RpYyB0aGF0IGlzIGNvbW1vbmx5IHVzZWQsIGFuZCBjYW4gYmUgY2FsY3VsYXRlZCBpbnN0YW50bHkuIEluIHRoaXMgZXhhbXBsZSwgd2UgbWlnaHQgaGF2ZSBiZWVuIGp1c3RpZmllZCBpbiBjaG9vc2luZyBhbnl0aGluZyBiZXR3ZWVuIFBDIDctMTIgYXMgYSBjdXRvZmYuIA0KDQpXZSBjaG9zZSAxMCBoZXJlLCBidXQgZW5jb3VyYWdlIHVzZXJzIHRvIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmc6DQoNCiogRGVuZHJpdGljIGNlbGwgYW5kIE5LIGFmaWNpb25hZG9zIG1heSByZWNvZ25pemUgdGhhdCBnZW5lcyBzdHJvbmdseSBhc3NvY2lhdGVkIHdpdGggUENzIDEyIGFuZCAxMyBkZWZpbmUgcmFyZSBpbW11bmUgc3Vic2V0cyAoaS5lLiBNWkIxIGlzIGEgbWFya2VyIGZvciBwbGFzbWFjeXRvaWQgRENzKS4gSG93ZXZlciwgdGhlc2UgZ3JvdXBzIGFyZSBzbyByYXJlLCB0aGV5IGFyZSBkaWZmaWN1bHQgdG8gZGlzdGluZ3Vpc2ggZnJvbSBiYWNrZ3JvdW5kIG5vaXNlIGZvciBhIGRhdGFzZXQgb2YgdGhpcyBzaXplIHdpdGhvdXQgcHJpb3Iga25vd2xlZGdlLiANCiogV2UgZW5jb3VyYWdlIHVzZXJzIHRvIHJlcGVhdCBkb3duc3RyZWFtIGFuYWx5c2VzIHdpdGggYSBkaWZmZXJlbnQgbnVtYmVyIG9mIFBDcyAoMTAsIDE1LCBvciBldmVuIDUwISkuIEFzIHlvdSB3aWxsIG9ic2VydmUsIHRoZSByZXN1bHRzIG9mdGVuIGRvIG5vdCBkaWZmZXIgZHJhbWF0aWNhbGx5Lg0KKiBXZSBhZHZpc2UgdXNlcnMgdG8gZXJyIG9uIHRoZSBoaWdoZXIgc2lkZSB3aGVuIGNob29zaW5nIHRoaXMgcGFyYW1ldGVyLiBGb3IgZXhhbXBsZSwgcGVyZm9ybWluZyBkb3duc3RyZWFtIGFuYWx5c2VzIHdpdGggb25seSA1IFBDcyBkb2VzIHNpZ25pZmljYW50bHkgYW5kIGFkdmVyc2VseSBhZmZlY3QgcmVzdWx0cy4NCg0KKioqDQoNCiMgQ2x1c3RlciB0aGUgY2VsbHMNCg0KU2V1cmF0ICBhcHBsaWVzIGEgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBhcHByb2FjaCwgYnVpbGRpbmcgdXBvbiBpbml0aWFsIHN0cmF0ZWdpZXMgaW4gKFtNYWNvc2tvICpldCBhbCpdKGh0dHA6Ly93d3cuY2VsbC5jb20vYWJzdHJhY3QvUzAwOTItODY3NCgxNSkwMDU0OS04KSkuIEltcG9ydGFudGx5LCB0aGUgKmRpc3RhbmNlIG1ldHJpYyogd2hpY2ggZHJpdmVzIHRoZSBjbHVzdGVyaW5nIGFuYWx5c2lzIChiYXNlZCBvbiBwcmV2aW91c2x5IGlkZW50aWZpZWQgUENzKSByZW1haW5zIHRoZSBzYW1lLiBIb3dldmVyLCBvdXIgYXBwcm9hY2ggdG8gcGFydGl0aW9uaW5nIHRoZSBjZWxsdWxhciBkaXN0YW5jZSBtYXRyaXggaW50byBjbHVzdGVycyBoYXMgZHJhbWF0aWNhbGx5IGltcHJvdmVkLiBPdXIgYXBwcm9hY2ggd2FzIGhlYXZpbHkgaW5zcGlyZWQgYnkgcmVjZW50IG1hbnVzY3JpcHRzIHdoaWNoIGFwcGxpZWQgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBhcHByb2FjaGVzIHRvIHNjUk5BLXNlcSBkYXRhIFtbU05OLUNsaXEsIFh1IGFuZCBTdSwgQmlvaW5mb3JtYXRpY3MsIDIwMTVdXShodHRwOi8vYmlvaW5mb3JtYXRpY3Mub3hmb3Jkam91cm5hbHMub3JnL2NvbnRlbnQvZWFybHkvMjAxNS8wMi8xMC9iaW9pbmZvcm1hdGljcy5idHYwODguYWJzdHJhY3QpIGFuZCBDeVRPRiBkYXRhIFtbUGhlbm9HcmFwaCwgTGV2aW5lICpldCBhbCouLCBDZWxsLCAyMDE1XV0oaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8yNjA5NTI1MSkuIEJyaWVmbHksIHRoZXNlIG1ldGhvZHMgZW1iZWQgY2VsbHMgaW4gYSBncmFwaCBzdHJ1Y3R1cmUgLSBmb3IgZXhhbXBsZSBhIEstbmVhcmVzdCBuZWlnaGJvciAoS05OKSBncmFwaCwgd2l0aCBlZGdlcyBkcmF3biBiZXR3ZWVuIGNlbGxzIHdpdGggc2ltaWxhciBmZWF0dXJlIGV4cHJlc3Npb24gcGF0dGVybnMsIGFuZCB0aGVuIGF0dGVtcHQgdG8gcGFydGl0aW9uIHRoaXMgZ3JhcGggaW50byBoaWdobHkgaW50ZXJjb25uZWN0ZWQgJ3F1YXNpLWNsaXF1ZXMnIG9yICdjb21tdW5pdGllcycuIA0KDQpBcyBpbiBQaGVub0dyYXBoLCB3ZSBmaXJzdCBjb25zdHJ1Y3QgYSBLTk4gZ3JhcGggYmFzZWQgb24gdGhlIGV1Y2xpZGVhbiBkaXN0YW5jZSBpbiBQQ0Egc3BhY2UsIGFuZCByZWZpbmUgdGhlIGVkZ2Ugd2VpZ2h0cyBiZXR3ZWVuIGFueSB0d28gY2VsbHMgYmFzZWQgb24gdGhlIHNoYXJlZCBvdmVybGFwIGluIHRoZWlyIGxvY2FsIG5laWdoYm9yaG9vZHMgKEphY2NhcmQgc2ltaWxhcml0eSkuIFRoaXMgc3RlcCBpcyBwZXJmb3JtZWQgdXNpbmcgdGhlIGBGaW5kTmVpZ2hib3JzKClgIGZ1bmN0aW9uLCBhbmQgdGFrZXMgYXMgaW5wdXQgdGhlIHByZXZpb3VzbHkgZGVmaW5lZCBkaW1lbnNpb25hbGl0eSBvZiB0aGUgZGF0YXNldCAoZmlyc3QgMTAgUENzKS4NCg0KVG8gY2x1c3RlciB0aGUgY2VsbHMsIHdlIG5leHQgYXBwbHkgbW9kdWxhcml0eSBvcHRpbWl6YXRpb24gdGVjaG5pcXVlcyBzdWNoIGFzIHRoZSBMb3V2YWluIGFsZ29yaXRobSAoZGVmYXVsdCkgb3IgU0xNIFtbU0xNLCBCbG9uZGVsICpldCBhbCouLCBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIE1lY2hhbmljc11dKGh0dHA6Ly9keC5kb2kub3JnLzEwLjEwODgvMTc0Mi01NDY4LzIwMDgvMTAvUDEwMDA4KSwgdG8gaXRlcmF0aXZlbHkgZ3JvdXAgY2VsbHMgdG9nZXRoZXIsIHdpdGggdGhlIGdvYWwgb2Ygb3B0aW1pemluZyB0aGUgc3RhbmRhcmQgbW9kdWxhcml0eSBmdW5jdGlvbi4gVGhlIGBGaW5kQ2x1c3RlcnMoKWAgZnVuY3Rpb24gaW1wbGVtZW50cyB0aGlzIHByb2NlZHVyZSwgYW5kIGNvbnRhaW5zIGEgcmVzb2x1dGlvbiBwYXJhbWV0ZXIgdGhhdCBzZXRzIHRoZSAnZ3JhbnVsYXJpdHknIG9mIHRoZSBkb3duc3RyZWFtIGNsdXN0ZXJpbmcsIHdpdGggaW5jcmVhc2VkIHZhbHVlcyBsZWFkaW5nIHRvIGEgZ3JlYXRlciBudW1iZXIgb2YgY2x1c3RlcnMuIFdlIGZpbmQgdGhhdCBzZXR0aW5nIHRoaXMgcGFyYW1ldGVyIGJldHdlZW4gMC40LTEuMiB0eXBpY2FsbHkgcmV0dXJucyBnb29kIHJlc3VsdHMgZm9yIHNpbmdsZS1jZWxsIGRhdGFzZXRzIG9mIGFyb3VuZCAzSyBjZWxscy4gT3B0aW1hbCByZXNvbHV0aW9uIG9mdGVuIGluY3JlYXNlcyBmb3IgbGFyZ2VyIGRhdGFzZXRzLiBUaGUgY2x1c3RlcnMgY2FuIGJlIGZvdW5kIHVzaW5nIHRoZSBgSWRlbnRzKClgIGZ1bmN0aW9uLg0KDQoNCmBgYHtyIGNsdXN0ZXJ9DQpwYm1jIDwtIEZpbmROZWlnaGJvcnMocGJtYywgZGltcyA9IDE6MTApDQpwYm1jIDwtIEZpbmRDbHVzdGVycyhwYm1jLCByZXNvbHV0aW9uID0gMC41KQ0KDQojIExvb2sgYXQgY2x1c3RlciBJRHMgb2YgdGhlIGZpcnN0IDUgY2VsbHMNCmhlYWQoSWRlbnRzKHBibWMpLCA1KQ0KYGBgDQoNCioqKg0KDQojIFJ1biBub24tbGluZWFyIGRpbWVuc2lvbmFsIHJlZHVjdGlvbiAoVU1BUC90U05FKQ0KDQpTZXVyYXQgb2ZmZXJzIHNldmVyYWwgbm9uLWxpbmVhciBkaW1lbnNpb25hbCByZWR1Y3Rpb24gdGVjaG5pcXVlcywgc3VjaCBhcyB0U05FIGFuZCBVTUFQLCB0byB2aXN1YWxpemUgYW5kIGV4cGxvcmUgdGhlc2UgZGF0YXNldHMuIFRoZSBnb2FsIG9mIHRoZXNlIGFsZ29yaXRobXMgaXMgdG8gbGVhcm4gdW5kZXJseWluZyBzdHJ1Y3R1cmUgaW4gdGhlIGRhdGFzZXQsIGluIG9yZGVyIHRvIHBsYWNlIHNpbWlsYXIgY2VsbHMgdG9nZXRoZXIgaW4gbG93LWRpbWVuc2lvbmFsIHNwYWNlLiBUaGVyZWZvcmUsIGNlbGxzIHRoYXQgYXJlIGdyb3VwZWQgdG9nZXRoZXIgd2l0aGluIGdyYXBoLWJhc2VkIGNsdXN0ZXJzIGRldGVybWluZWQgYWJvdmUgc2hvdWxkIGNvLWxvY2FsaXplIG9uIHRoZXNlIGRpbWVuc2lvbiByZWR1Y3Rpb24gcGxvdHMuIA0KDQpXaGlsZSB3ZSBhbmQgb3RoZXJzIGhhdmUgcm91dGluZWx5IGZvdW5kIDJEIHZpc3VhbGl6YXRpb24gdGVjaG5pcXVlcyBsaWtlIHRTTkUgYW5kIFVNQVAgdG8gYmUgdmFsdWFibGUgdG9vbHMgZm9yIGV4cGxvcmluZyBkYXRhc2V0cywgYWxsIHZpc3VhbGl6YXRpb24gdGVjaG5pcXVlcyBoYXZlIGxpbWl0YXRpb25zLCBhbmQgY2Fubm90IGZ1bGx5IHJlcHJlc2VudCB0aGUgY29tcGxleGl0eSBvZiB0aGUgdW5kZXJseWluZyBkYXRhLiBJbiBwYXJ0aWN1bGFyLCB0aGVzZSBtZXRob2RzIGFpbSB0byBwcmVzZXJ2ZSBsb2NhbCBkaXN0YW5jZXMgaW4gdGhlIGRhdGFzZXQgKGkuZS4gZW5zdXJpbmcgdGhhdCBjZWxscyB3aXRoIHZlcnkgc2ltaWxhciBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZXMgY28tbG9jYWxpemUpLCBidXQgb2Z0ZW4gZG8gbm90IHByZXNlcnZlIG1vcmUgZ2xvYmFsIHJlbGF0aW9uc2hpcHMuIFdlIGVuY291cmFnZSB1c2VycyB0byBsZXZlcmFnZSB0ZWNobmlxdWVzIGxpa2UgVU1BUCBmb3IgdmlzdWFsaXphdGlvbiwgYnV0IHRvIGF2b2lkIGRyYXdpbmcgYmlvbG9naWNhbCBjb25jbHVzaW9ucyBzb2xlbHkgb24gdGhlIGJhc2lzIG9mIHZpc3VhbGl6YXRpb24gdGVjaG5pcXVlcy4gDQoNCg0KDQpgYGB7ciB1bWFwfQ0KcGJtYyA8LSBSdW5VTUFQKHBibWMsIGRpbXMgPSAxOjEwKQ0KYGBgDQoNCmBgYHtyIHVtYXBwbG90LCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQ0KIyBub3RlIHRoYXQgeW91IGNhbiBzZXQgYGxhYmVsID0gVFJVRWAgb3IgdXNlIHRoZSBMYWJlbENsdXN0ZXJzIGZ1bmN0aW9uIHRvIGhlbHAgbGFiZWwgaW5kaXZpZHVhbCBjbHVzdGVycw0KRGltUGxvdChwYm1jLCByZWR1Y3Rpb24gPSAndW1hcCcpDQpgYGANCg0KWW91IGNhbiBzYXZlIHRoZSBvYmplY3QgYXQgdGhpcyBwb2ludCBzbyB0aGF0IGl0IGNhbiBlYXNpbHkgYmUgbG9hZGVkIGJhY2sgaW4gd2l0aG91dCBoYXZpbmcgdG8gcmVydW4gdGhlIGNvbXB1dGF0aW9uYWxseSBpbnRlbnNpdmUgc3RlcHMgcGVyZm9ybWVkIGFib3ZlLCBvciBlYXNpbHkgc2hhcmVkIHdpdGggY29sbGFib3JhdG9ycy4NCg0KYGBge3Igc2F2ZW9iamVjdCwgZXZhbD1GQUxTRX0NCnNhdmVSRFMocGJtYywgZmlsZSA9ICJkYXRhL3BibWNfdHV0b3JpYWwucmRzIikNCmBgYA0KDQoqKioNCg0KIyBGaW5kaW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBmZWF0dXJlcyAoY2x1c3RlciBiaW9tYXJrZXJzKQ0KDQpTZXVyYXQgY2FuIGhlbHAgeW91IGZpbmQgbWFya2VycyB0aGF0IGRlZmluZSBjbHVzdGVycyB2aWEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKS4gQnkgZGVmYXVsdCwgaXQgaWRlbnRpZmllcyBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgbWFya2VycyBvZiBhIHNpbmdsZSBjbHVzdGVyIChzcGVjaWZpZWQgaW4gYGlkZW50LjFgKSwgY29tcGFyZWQgdG8gYWxsIG90aGVyIGNlbGxzLiAgYEZpbmRBbGxNYXJrZXJzKClgIGF1dG9tYXRlcyB0aGlzIHByb2Nlc3MgZm9yIGFsbCBjbHVzdGVycywgYnV0IHlvdSBjYW4gYWxzbyB0ZXN0IGdyb3VwcyBvZiBjbHVzdGVycyB2cy4gZWFjaCBvdGhlciwgb3IgYWdhaW5zdCBhbGwgY2VsbHMuDQoNCkluIFNldXJhdCB2NSwgd2UgdXNlIHRoZSBwcmVzdG8gcGFja2FnZSAoYXMgZGVzY3JpYmVkIFtoZXJlXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvNjUzMjUzdjEpIGFuZCBhdmFpbGFibGUgZm9yIGluc3RhbGxhdGlvbiBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2ltbXVub2dlbm9taWNzL3ByZXN0bykpLCB0byBkcmFtYXRpY2FsbHkgaW1wcm92ZSB0aGUgc3BlZWQgb2YgREUgYW5hbHlzaXMsIHBhcnRpY3VsYXJseSBmb3IgbGFyZ2UgZGF0YXNldHMuIEZvciB1c2VycyB3aG8gYXJlIG5vdCB1c2luZyBwcmVzdG8sIHlvdSBjYW4gZXhhbWluZSB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhpcyBmdW5jdGlvbiAoYD9GaW5kTWFya2Vyc2ApIHRvIGV4cGxvcmUgdGhlIGBtaW4ucGN0YCBhbmQgYGxvZ2ZjLnRocmVzaG9sZGAgcGFyYW1ldGVycywgd2hpY2ggY2FuIGJlIGluY3JlYXNlZCBpbiBvcmRlciB0byBpbmNyZWFzZSB0aGUgc3BlZWQgb2YgREUgdGVzdGluZy4NCg0KYGBge3IgbWFya2VyczF9DQojIGZpbmQgYWxsIG1hcmtlcnMgb2YgY2x1c3RlciAyDQpjbHVzdGVyMi5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHBibWMsIGlkZW50LjEgPSAyKQ0KaGVhZChjbHVzdGVyMi5tYXJrZXJzLCBuID0gNSkNCiMgZmluZCBhbGwgbWFya2VycyBkaXN0aW5ndWlzaGluZyBjbHVzdGVyIDUgZnJvbSBjbHVzdGVycyAwIGFuZCAzDQpjbHVzdGVyNS5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHBibWMsIGlkZW50LjEgPSA1LCBpZGVudC4yID0gYygwLCAzKSkNCmhlYWQoY2x1c3RlcjUubWFya2VycywgbiA9IDUpDQojIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLCByZXBvcnQgb25seSB0aGUgcG9zaXRpdmUgb25lcw0KcGJtYy5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHBibWMsIG9ubHkucG9zID0gVFJVRSkNCnBibWMubWFya2VycyAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2cyRkMgPiAxKQ0KYGBgDQoNClNldXJhdCBoYXMgc2V2ZXJhbCB0ZXN0cyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2hpY2ggY2FuIGJlIHNldCB3aXRoIHRoZSB0ZXN0LnVzZSBwYXJhbWV0ZXIgKHNlZSBvdXIgW0RFIHZpZ25ldHRlXShkZV92aWduZXR0ZS5odG1sKSBmb3IgZGV0YWlscykuIEZvciBleGFtcGxlLCB0aGUgUk9DIHRlc3QgcmV0dXJucyB0aGUgJ2NsYXNzaWZpY2F0aW9uIHBvd2VyJyBmb3IgYW55IGluZGl2aWR1YWwgbWFya2VyIChyYW5naW5nIGZyb20gMCAtIHJhbmRvbSwgdG8gMSAtIHBlcmZlY3QpLg0KDQpgYGB7ciBtYXJrZXJzcm9jfQ0KY2x1c3RlcjAubWFya2VycyA8LSBGaW5kTWFya2VycyhwYm1jLCBpZGVudC4xID0gMCwgbG9nZmMudGhyZXNob2xkID0gMC4yNSwgdGVzdC51c2UgPSAicm9jIiwgb25seS5wb3MgPSBUUlVFKQ0KYGBgDQoNCldlIGluY2x1ZGUgc2V2ZXJhbCB0b29scyBmb3IgdmlzdWFsaXppbmcgbWFya2VyIGV4cHJlc3Npb24uIGBWbG5QbG90KClgIChzaG93cyBleHByZXNzaW9uIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMgYWNyb3NzIGNsdXN0ZXJzKSwgYW5kIGBGZWF0dXJlUGxvdCgpYCAodmlzdWFsaXplcyBmZWF0dXJlIGV4cHJlc3Npb24gb24gYSB0U05FIG9yIFBDQSBwbG90KSBhcmUgb3VyIG1vc3QgY29tbW9ubHkgdXNlZCB2aXN1YWxpemF0aW9ucy4gV2UgYWxzbyBzdWdnZXN0IGV4cGxvcmluZyBgUmlkZ2VQbG90KClgLCBgQ2VsbFNjYXR0ZXIoKWAsIGFuZCBgRG90UGxvdCgpYCBhcyBhZGRpdGlvbmFsIG1ldGhvZHMgdG8gdmlldyB5b3VyIGRhdGFzZXQuDQoNCmBgYHtyIG1hcmtlcnBsb3RzLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0NClZsblBsb3QocGJtYywgZmVhdHVyZXMgPSBjKCJNUzRBMSIsICJDRDc5QSIpKQ0KIyB5b3UgY2FuIHBsb3QgcmF3IGNvdW50cyBhcyB3ZWxsDQpWbG5QbG90KHBibWMsIGZlYXR1cmVzID0gYygiTktHNyIsICJQRjQiKSwgc2xvdCA9ICdjb3VudHMnLCBsb2cgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0NCkZlYXR1cmVQbG90KHBibWMsIGZlYXR1cmVzID0gYygiTVM0QTEiLCAiR05MWSIsICJDRDNFIiwgIkNEMTQiLCAiRkNFUjFBIiwgIkZDR1IzQSIsICJMWVoiLCAiUFBCUCIsICJDRDhBIikpDQpgYGANCmBEb0hlYXRtYXAoKWAgZ2VuZXJhdGVzIGFuIGV4cHJlc3Npb24gaGVhdG1hcCBmb3IgZ2l2ZW4gY2VsbHMgYW5kIGZlYXR1cmVzLiBJbiB0aGlzIGNhc2UsIHdlIGFyZSBwbG90dGluZyB0aGUgdG9wIDEwIG1hcmtlcnMgKG9yIGFsbCBtYXJrZXJzIGlmIGxlc3MgdGhhbiAxMCkgZm9yIGVhY2ggY2x1c3Rlci4NCg0KYGBge3IgY2x1c3RlckhlYXRtYXAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQ0KcGJtYy5tYXJrZXJzICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZzJGQyA+IDEpICU+JQ0KICAgIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUNCiAgICB1bmdyb3VwKCkgLT4gdG9wMTANCkRvSGVhdG1hcChwYm1jLCBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCmBgYHtyIHJpZGdlcGxvdCwgIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEyfQ0KDQojIERlZmluZSB0aGUgZmVhdHVyZXMgKGdlbmVzKSB5b3Ugd2FudCB0byBwbG90DQojIE5vdGU6IENEM0QgaXMgYSBULWNlbGwgbWFya2VyLCBGQ0dSM0EgaXMgYSBtYXJrZXIgZm9yIEZDR1IzQSsgTW9ub2N5dGVzLA0KIyBhbmQgR05MWSBpcyBhIG1hcmtlciBmb3IgTmF0dXJhbCBLaWxsZXIgY2VsbHMNCmZlYXR1cmVzX3RvX3Bsb3QgPC0gYygiQ0QzRCIsICJGQ0dSM0EiLCAiR05MWSIpDQoNCiMgQ3JlYXRlIGEgc3RhbmRhcmQgcmlkZ2UgcGxvdA0KUmlkZ2VQbG90KG9iamVjdCA9IHBibWMsIGZlYXR1cmVzID0gZmVhdHVyZXNfdG9fcGxvdCkNCg0KIyBDcmVhdGUgYSBzdGFja2VkIHJpZGdlIHBsb3QgZm9yIGNvbXBhcmlzb24NClJpZGdlUGxvdCgNCiAgb2JqZWN0ID0gcGJtY19zbWFsbCwNCiAgZmVhdHVyZXMgPSBmZWF0dXJlc190b19wbG90LA0KICBzdGFjayA9IFRSVUUsDQogIGNvbHMgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIpDQopDQoNCmBgYA0KDQpgYGB7ciBjZWxsc2NhdHRlciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9DQoNCiMgQ29tcGFyZSB0d28gaW5kaXZpZHVhbCBjZWxscyBmcm9tIHRoZSBkYXRhc2V0DQpDZWxsU2NhdHRlcigNCiAgb2JqZWN0ID0gcGJtYywNCiAgY2VsbDEgPSAiQUFBQ0FUQUNBQUNDQUMtMSIsDQogIGNlbGwyID0gIkFBQUNBVFRHQUdDVEFDLTEiDQopDQpgYGANCg0KYGBge3IgZG90cGxvdCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9DQojIENyZWF0ZSBhIERvdFBsb3QgZm9yIGEgc2V0IG9mIG1hcmtlciBnZW5lcyBhY3Jvc3MgdGhlIGRlZmF1bHQgaWRlbnRpdHkgY2xhc3Nlcw0KRG90UGxvdCgNCiAgb2JqZWN0ID0gcGJtYywNCiAgZmVhdHVyZXMgPSBjKCJNUzRBMSIsICJDRDc5QSIsICJDRDNEIiwgIkNDUjciLCAiQ0QxNCIsICJGQ0VSMUEiKQ0KKQ0KYGBgDQoNCg0KKioqDQojIEFzc2lnbmluZyBjZWxsIHR5cGUgaWRlbnRpdHkgdG8gY2x1c3RlcnMNCg0KRm9ydHVuYXRlbHkgaW4gdGhlIGNhc2Ugb2YgdGhpcyBkYXRhc2V0LCB3ZSBjYW4gdXNlIGNhbm9uaWNhbCBtYXJrZXJzIHRvIGVhc2lseSBtYXRjaCB0aGUgdW5iaWFzZWQgY2x1c3RlcmluZyB0byBrbm93biBjZWxsIHR5cGVzOg0KDQpDbHVzdGVyIElEIHwgTWFya2VycyAgICAgICB8IENlbGwgVHlwZQ0KLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0NCjAgICAgICAgICAgfCBJTDdSLCBDQ1I3ICAgIHwgTmFpdmUgQ0Q0KyBUIA0KMSAgICAgICAgICB8IENEMTQsIExZWiAgICAgfCBDRDE0KyBNb25vDQoyICAgICAgICAgIHwgSUw3UiwgUzEwMEE0ICB8IE1lbW9yeSBDRDQrIA0KMyAgICAgICAgICB8IE1TNEExICAgICAgICAgfCBCIA0KNCAgICAgICAgICB8IENEOEEgICAgICAgICAgfCBDRDgrIFQgDQo1ICAgICAgICAgIHwgRkNHUjNBLCBNUzRBNyB8IEZDR1IzQSsgTW9ubw0KNiAgICAgICAgICB8IEdOTFksIE5LRzcgICAgfCBOSyANCjcgICAgICAgICAgfCBGQ0VSMUEsIENTVDMgIHwgREMNCjggICAgICAgICAgfCBQUEJQICAgICAgICAgIHwgUGxhdGVsZXQNCg0KDQpgYGB7ciBsYWJlbHBsb3QsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTl9DQpuZXcuY2x1c3Rlci5pZHMgPC0gYygiTmFpdmUgQ0Q0IFQiLCAiQ0QxNCsgTW9ubyIsICJNZW1vcnkgQ0Q0IFQiLCAiQiIsICJDRDggVCIsICJGQ0dSM0ErIE1vbm8iLCAiTksiLCAiREMiLCAiUGxhdGVsZXQiKQ0KbmFtZXMobmV3LmNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMocGJtYykNCnBibWMgPC0gUmVuYW1lSWRlbnRzKHBibWMsIG5ldy5jbHVzdGVyLmlkcykNCkRpbVBsb3QocGJtYywgcmVkdWN0aW9uID0gJ3VtYXAnLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjUpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCmBgYHtyIHNhdmUuaW1nLCBpbmNsdWRlPVRSVUUsZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OSB9DQpsaWJyYXJ5KGdncGxvdDIpDQpwbG90IDwtIERpbVBsb3QocGJtYywgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA0LjUpICsgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIA0KICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsgDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMTApKSkNCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9vdXRwdXQvaW1hZ2VzL3BibWMza191bWFwLnBuZyIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTIsIHBsb3QgPSBwbG90LCBkcGkgPSAzMDApDQoNCmBgYA0KDQpgYGB7ciBzYXZlLnJkcywgZXZhbD1GQUxTRX0NCnNhdmVSRFMocGJtYywgZmlsZSA9ICIuLi9vdXRwdXQvcGJtYzNrX2ZpbmFsLnJkcyIpDQpgYGANCg0KYGBge3Igc2F2ZS50aW1lcywgaW5jbHVkZSA9IEZBTFNFLCBldmFsPUZBTFNFfQ0Kd3JpdGUuY3N2KHggPSB0KGFzLmRhdGEuZnJhbWUoYWxsX3RpbWVzKSksIGZpbGUgPSAiLi4vb3V0cHV0L3RpbWluZ3MvcGJtYzNrX3R1dG9yaWFsX3RpbWVzLmNzdiIpDQpgYGANCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PioqU2Vzc2lvbiBJbmZvKio8L3N1bW1hcnk+DQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KPC9kZXRhaWxzPg0K